<?php

namespace common\components\easy_wechat;

use common\components\wtools\tools\Tools;
use EasyWeChat\Factory;
use GuzzleHttp\Client;
use Symfony\Component\Cache\Adapter\RedisAdapter;
use yii\base\Component;

/**
 * @property-read \EasyWeChat\OfficialAccount\Application $gzhApp
 * @property-read \EasyWeChat\MiniProgram\Application $xcxApp
 *
 * @property string $xcxAccessToken
 */
class EasyWechat extends Component
{
    public $type = 1; // 1:公众号 2:小程序 3:小游戏
    public $token; // 令牌(Token)
    public $encodingAESKey; // 消息加解密密钥
    public $appID; // 开发者ID是公众号开发识别码，配合开发者密码可调用公众号的接口能力。
    public $appSecret; // 开发者密码是校验公众号开发者身份的密码，具有极高的安全性。切记勿把密码直接交给第三方开发者或直接存储在代码中。如需第三方代开发公众号，请使用授权方式接入。
    public $responseType = "array"; // 指定 API 调用返回结果的类型：array(default)/collection/object/raw/自定义类名

    public $payMchId; // 微信支付商户号

    const MSG_TYPE_EVENT = "event"; // 事件消息
    const MSG_TYPE_TEXT  = "text"; // 文字消息
    const MSG_TYPE_IMAGE  = "image"; // 图片消息
    const MSG_TYPE_VOICE  = "voice"; // 语音消息
    const MSG_TYPE_VIDEO  = "video"; // 视频消息
    const MSG_TYPE_LOCATION  = "location"; // 坐标消息
    const MSG_TYPE_LINK  = "link"; // 链接消息
    const MSG_TYPE_FILE  = "file"; // 文件消息

    const EVENT_TYPE_SUBSCRIBE = "subscribe"; // 关注
    const EVENT_TYPE_UNSUBSCRIBE = "unsubscribe"; // 取消关注
    const EVENT_TYPE_CLICK = "CLICK"; // 点击

    /**
     * @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
     * @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
     * @throws \GuzzleHttp\Exception\GuzzleException
     * @throws \ReflectionException
     * @return \EasyWeChat\OfficialAccount\Application
     */
    public function getGzhApp()
    {
        if ($this->type != 1) {
            throw new \Exception("公众号类型必须为1，当前类型是{$this->type}");
        }
        $config = [
            'app_id' => $this->appID,
            'secret' => $this->appSecret,
            'token' => $this->token,
            'aes_key' => $this->encodingAESKey,
            'response_type' => $this->responseType,
            'log' => [
                'default' => 'dev', // 默认使用的 channel，生产环境可以改为下面的 prod
                'channels' => [
                    // 测试环境
                    'dev' => [
                        'driver' => 'single',
                        'path' => \Yii::$app->runtimePath."/gzh/easywechat.log",
                        'level' => 'debug',
                    ],
                    // 生产环境
                    'prod' => [
                        'driver' => 'daily',
                        'path' => \Yii::$app->runtimePath."/gzh/easywechat.log",
                        'level' => 'info',
                    ],
                ],
            ],
        ];
        $app = Factory::officialAccount($config);
        $app->rebind('cache', new Cache());
//        $app->menu->create($this->buttons);
//        $server = $app->server;
//        $server->push(function ($message){
//            Tools::yiiLog($message);
//            return $this->registerMsgHandle($message);
//        });
        return $app;
    }

    /**
     * @return \EasyWeChat\MiniProgram\Application
     * @throws \Exception
     */
    public function getXcxApp()
    {
        if ($this->type != 2) {
            throw new \Exception("小程序类型必须为2，当前类型是{$this->type}");
        }
        $config = [
            'app_id' => $this->appID,
            'secret' => $this->appSecret,
//            'response_type' => 'array',
            'log' => [
                'level' => 'debug',
                'file' => \Yii::$app->runtimePath.'/xcx/wechat.log',
            ],
        ];
        $app = Factory::miniProgram($config);
        $app->rebind('cache', new Cache());
        return $app;
    }

    /**
     * @return mixed
     * @throws \EasyWeChat\Kernel\Exceptions\HttpException
     * @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
     * @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
     * @throws \EasyWeChat\Kernel\Exceptions\RuntimeException
     * @throws \Psr\SimpleCache\InvalidArgumentException
     */
    public function getXcxAccessToken()
    {
        /*$cacheKey = "ewXcx-".$this->appID;
        $access_token = \Yii::$app->cache->get($cacheKey);
        if (!$access_token) {
            $info = $this->xcxApp->access_token->getToken();
            $access_token = $info['access_token'];
            $expires_in = $info['expires_in'] - 10;
            \Yii::$app->cache->set($cacheKey, $access_token, $expires_in);
        }*/
        $info = $this->xcxApp->access_token->getToken();
        $access_token = $info['access_token'];
        return $access_token;
    }

    /**
     * @param $mp_appid
     * @param $mp_code
     * @param $encryptedData
     * @param $iv
     * @param $signature
     * @return array
     * @throws \EasyWeChat\Kernel\Exceptions\DecryptException
     * @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
     */
    public function decryptXcxData($mp_appid, $mp_code, $encryptedData, $iv, $signature, $rawData)
    {
        if ($mp_appid !== $this->appID) {
            $errMsg = "appid错误";
            throw new \Exception($errMsg);
        }
        $res1 = $this->xcxApp->auth->session($mp_code);
        $openid = $res1['openid'];
        $sessionKey = $res1['session_key'];
        $sign = sha1($rawData.$sessionKey);
        if ($sign !== $signature) {
            $errMsg = "签名错误";
            throw new \Exception($errMsg);
        }
        $res2 = $this->xcxApp->encryptor->decryptData($sessionKey, $iv, $encryptedData);
        if (isset($res2['openId']) && $openid !== $res2['openId']) {
            $errMsg = "openid异常";
            throw new \Exception($errMsg);
        }
        return $res2;
    }

    /**
     * @param $code
     * @return mixed
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    public function decryptXcxMoblie($code)
    {
        $url = "https://api.weixin.qq.com/wxa/business/getuserphonenumber";
        $clent = new Client();
        $resp = $clent->request('POST', $url, [
            'query' => [
                'access_token' => $this->xcxAccessToken,
            ],
            'json' => [
                'code' => $code,
            ],
        ]);
        $resp_cont = $resp->getBody()->getContents();
        $resp_arr = Tools::isJson($resp_cont);
        if (!isset($resp_arr['errcode'])) {
            throw new \Exception("获取手机号失败，错误信息：{$resp_cont}");
        }
        if ($resp_arr['errcode'] !== 0) {
            throw new \Exception("获取手机号失败，错误信息：{$resp_arr['errmsg']}");
        }
        return $resp_arr['phone_info'];
    }
}
